package com.aisi.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import java.util.Calendar;
import java.util.Date;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @Author: shenjianZ
 * @Date: 2024/2/14 13:32
 * @Description: JWT工具类
 */

@Component
public class JwtUtils {

    @Value("${spring.security.jwt.key}")
    public String key; // JWT 密钥

    @Value("${spring.security.jwt.expire}")
    public int expire; // JWT 令牌默认过期时间

    @Resource
    private StringRedisTemplate template;

    /**
     * 为用户创建 JWT 的令牌(Token)
     * @param details User
     * @param id User id
     * @param username User name
     * @return JWT令牌
     */
    public String createJwt(UserDetails details, int id, String username) {
        System.out.println(key);
        Algorithm algorithm = Algorithm.HMAC256(key); // 令牌使用算法
        Date expireTime = expireTime(); // 令牌过期时间
        return JWT.create()
                .withJWTId(UUID.randomUUID().toString())
                .withClaim("id", id)
                .withClaim("name", username)
                .withClaim("authorities", details.getAuthorities().stream().map(GrantedAuthority::getAuthority).toList())
                .withExpiresAt(expireTime)
                .withIssuedAt(new Date())
                .sign(algorithm);
    }

    /**
     * 计算令牌过期时间
     * @return Date
     */
    public Date expireTime() {
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.HOUR, expire * 24);
        return calendar.getTime();
    }

    /**
     * 根据Token解析得到JWT的令牌
     * @param headerToken 用户的Token
     * @return DecodedJWT
     *         若Token为空、令牌被篡改、令牌已过期，返回null
     *         否则，返回解析的JWT令牌对象
     */
    public DecodedJWT resolveJwt(String headerToken){
        String token = convertToken(headerToken); //得到token
        if (token == null) return  null;
        Algorithm algorithm = Algorithm.HMAC256(key);
        JWTVerifier jwtVerifier = JWT.require(algorithm).build();
        try {
            DecodedJWT verify = jwtVerifier.verify(token); // 在try catch中检查令牌是否被篡改
            Boolean isInvalidToken = isInvalidToken(verify.getId());
            // 如如果Token已过期，令牌为null
            if (Boolean.TRUE.equals(isInvalidToken))
                return null;
            Date expires = verify.getExpiresAt();
            return new Date().after(expires) ? null : verify;
        }catch (JWTVerificationException e){
            return null;
        }
    }

    /**
     * 去除Token的前缀
     * @param token Token
     * @return String
     */
    public String convertToken(String token){
        if(token == null || ! token.startsWith("Bearer "))
            return null;
        return token.replace("Bearer ","");
    }

    /**
     * 根据JWT令牌解析用户信息
     * @param jwt DecodedJWT
     * @return UserDetails
     */
    public UserDetails toUser(DecodedJWT jwt){
        Map<String, Claim> claims = jwt.getClaims();
        return User
                .withUsername(claims.get("name").asString())
                .password("ccccccccc")
                .authorities(claims.get("authorities").asArray(String.class))
                .build();
    }


    /**
     * 根据JWT令牌解析用户ID
     * @param jwt DecodedJWT
     * @return id
     */
    public Integer toId(DecodedJWT jwt){
        Map<String, Claim> claims = jwt.getClaims();
        return claims.get("id").asInt();
    }

    /**
     * 验证Token是否过期
     * @param uuid UUID
     * @return boolean
     */
    private Boolean isInvalidToken(String uuid) {
        return template.hasKey(Const.JWT_BLACK_LIST_PREFIX + uuid);
    }

    /**
     * 让令牌失效
     * @param headerToken Token
     * @return
     */
    public Boolean invalidateJwt(String headerToken){
        String token = convertToken(headerToken);
        if(token == null ) return false; // 令牌不存在
        Algorithm algorithm = Algorithm.HMAC256(key);
        JWTVerifier verifier = JWT.require(algorithm).build();
        try {
            DecodedJWT jwt = verifier.verify(token);
            String uuid = jwt.getId();
            return deleteToken(uuid,jwt.getExpiresAt()); //返回删除结果
        }catch (JWTVerificationException e){
            return false; // 令牌验证未通过
        }
    }

    /**
     * 删除redis的令牌Token
     * @param uuid UUID
     * @param expiresAt 过期时间
     * @return
     */
    private Boolean deleteToken(String uuid, Date expiresAt) {
        Boolean isInvalidToken = isInvalidToken(uuid);
        // 如果Token已过期，无需删除redis的Token
        if (Boolean.TRUE.equals(isInvalidToken))
            return false;
        // expire 不能为0
        long expire = Math.max(0,expiresAt.getTime() - new Date().getTime());
        // 删除redis中的Token
        template.opsForValue().set(Const.JWT_BLACK_LIST_PREFIX+uuid ,"",expire, TimeUnit.MILLISECONDS);
        return true;
    }


}
